import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.joda.time.DateTime;
import org.joda.time.DateTimeComparator;
import org.joda.time.IllegalFieldValueException;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.DateTimeFormatterBuilder;
import org.joda.time.format.DateTimeParser;


public class Search implements Command {
	
	public ArrayList<Task> tasks;
	public AddParser addParser;
	public Storage storage;
	public History history;
	public Logger logger;
	private boolean isError = true;
	String data;
	String errorMessage;
	String output;
	
	// Magic Strings for different search mode
	private static final String SEARCH_DATE = "-date";
	private static final String SEARCH_FROM = "-from";
	private static final String SEARCH_TO = "-to";
	private static final String SEARCH_PRIORITY = "-!";
	private static final String SEARCH_LABEL = "-label";
	private static final String SEARCH_DONE = "-done";
	private static final String SEARCH_UNDONE = "-undone";
	
	
	private static final int BASEINDEX = 1;
	private static final String DESIRED_TASK = "%d. %s\n";
	private static final String ERROR_INVALID_DATE = "Date entered is invalid.\n";
	
	// messages for search
	private static final String SEARCH_UNSUCCESSFUL = "Search is unsuccessful\n";
	private static final String SEARCH_BY_CONTENT = "Searching \"%s\"\n";
	private static final String SEARCH_BY_PRIORITY = "Searching tasks with priority:\n";
	private static final String SEARCH_BY_LABEL = "Searching tasks with label: %s\n";
	private static final String SEARCH_BY_DONE = "Searching tasks which are Done:\n";
	private static final String SEARCH_BY_UNDONE = "Searching tasks which are Undone:\n";
	private static final String SEARCH_START = "%d. %s starting from %02d:%02d\n";
	private static final String SEARCH_END = "%d. %s ending on %02d:%02d\n";
	private static final String SEARCH_DATE_FORMAT = "%d. %s by %02d:%02d\n";
	
	public Search(ArrayList<Task> tasks, Storage storage, History history,Logger logger) {
		this.tasks = tasks;
		this.addParser = new AddParser();
		this.storage = storage;
		this.history = history;
		this.logger = logger;
		errorMessage = "";
	}
	
	@Override
	public void acceptInput(String data) {
		// TODO Auto-generated method stub
		this.data = data;
		
	}

	@Override
	public void execute() {
		// TODO Auto-generated method stub
		String searchMode = getSearchMode(data);
		String searchResults;
		switch (searchMode) {
		case SEARCH_DATE:
			searchResults = searchDate(data);
			break;
		case SEARCH_FROM:
			searchResults = searchFrom(data);
			break;
		case SEARCH_TO:
			searchResults = searchTo(data);
			break;
		case SEARCH_PRIORITY:
			searchResults = searchPriority();
			break;
		case SEARCH_LABEL:
			searchResults = searchLabel(data);
			break;
		case SEARCH_DONE:
			searchResults = searchDone();
			break;
		case SEARCH_UNDONE:
			searchResults = searchUndone();
			break;
		default:
			searchResults = searchContent(data);
		}
		
		output = searchResults;
		
	}

	@Override
	public String save() throws IOException {
		// TODO Auto-generated method stub
		return storage.writeToStorage(tasks);
	}

	@Override
	public String outputToUser() {
		// TODO Auto-generated method stub
		return output;
	}
	
	private String searchDate(String data) {
		String searchResults = SEARCH_UNSUCCESSFUL;
		DateTime searchDate;
		DateTime deadline;
		DateTimeComparator compareDate = DateTimeComparator.getInstance();
		StringBuilder searchDateBuilder = new StringBuilder();
		boolean foundDate = false;
		try {
			searchDate = obtainDate(data);
		} catch (ParseException e) {
			// TODO Auto-generated catch block
			errorMessage = e.getMessage();
			logger.log(Level.INFO, errorMessage);
			e.printStackTrace();
			return errorMessage;
		}

		for (Task task : tasks) {
			deadline = task.getDeadline();
			if (task.getDeadline() != null
					&& compareDate.compare(task.getDeadline(), searchDate) == 0) {
				searchDateBuilder.append(String.format(SEARCH_DATE_FORMAT,
						tasks.indexOf(task) + BASEINDEX, task.getContent(), deadline.getHourOfDay(),deadline.getMinuteOfDay() % 60));
				foundDate = true;
			}
		}
		if (foundDate) {
			searchResults = searchDateBuilder.toString();
			isError = false;
		}

		return searchResults;

	}

	private String searchFrom(String data) {
		String searchResults = SEARCH_UNSUCCESSFUL;
		DateTime searchFrom;
		DateTimeComparator compareDate = DateTimeComparator
				.getDateOnlyInstance();
		try {
			searchFrom = obtainDate(data);
		} catch (ParseException e) {
			// TODO Auto-generated catch block
			errorMessage = e.getMessage();
			logger.log(Level.INFO, errorMessage);
			e.printStackTrace();
			return errorMessage;
		}
		StringBuilder searchFromBuilder = new StringBuilder();
		boolean foundDate = false;
		for (Task task : tasks) {
			if (task.getStart() != null
					&& compareDate.compare(task.getStart(), searchFrom) == 0) {
				searchFromBuilder.append(String.format(SEARCH_START,tasks.indexOf(task)+BASEINDEX, task
						.getContent(), task.getStart().getHourOfDay(), task
						.getStart().getMinuteOfDay() % 60));
				foundDate = true;
			}
		}
		if (foundDate) {
			searchResults = searchFromBuilder.toString();
			isError = false;
		}

		return searchResults;
	}

	private String searchTo(String data) {
		String searchResults = SEARCH_UNSUCCESSFUL;
		DateTime searchTo;
		DateTimeComparator compareDate = DateTimeComparator
				.getDateOnlyInstance();
		try {
			searchTo = obtainDate(data);
		} catch (ParseException e) {
			// TODO Auto-generated catch block
			errorMessage = e.getMessage();
			logger.log(Level.INFO, errorMessage);
			e.printStackTrace();
			return errorMessage;
		}
		StringBuilder searchToBuilder = new StringBuilder();
		boolean foundDate = false;

		for (Task task : tasks) {
			if (task.getEnd() != null
					&& compareDate.compare(task.getEnd(), searchTo) == 0) {
				searchToBuilder.append(String.format(SEARCH_END,tasks.indexOf(task)+BASEINDEX, task
						.getContent(), task.getEnd().getHourOfDay(), task
						.getEnd().getMinuteOfDay() % 60));
				foundDate = true;
			}
		}
		if (foundDate) {
			searchResults = searchToBuilder.toString();
			isError = false;
		}

		return searchResults;
	}

	private String searchPriority() {
		boolean noPrioritisedTask = true;
		String allTasks = SEARCH_UNSUCCESSFUL;
		StringBuilder obtainAllTasks = new StringBuilder();

		obtainAllTasks.append(SEARCH_BY_PRIORITY);

		for (Task task : tasks) {
			if (task.isPriority()) {
				obtainAllTasks.append(String.format(DESIRED_TASK,
						tasks.indexOf(task) + BASEINDEX, task.getContent()));
				noPrioritisedTask = false;
			}
		}

		if (!noPrioritisedTask) {
			allTasks = obtainAllTasks.toString();
			allTasks.trim();
			isError = false;
			return allTasks;
		} else {
			return allTasks;
		}
	}

	private String searchDone() {
		
		boolean noDoneTask = true;
		String allTasks = SEARCH_UNSUCCESSFUL;
		StringBuilder obtainAllTasks = new StringBuilder();

		obtainAllTasks.append(SEARCH_BY_DONE);

		for (Task task : tasks) {
			if (task.isDone()) {
				obtainAllTasks.append(String.format(DESIRED_TASK,
						tasks.indexOf(task) + BASEINDEX, task.getContent()));
				noDoneTask = false;
			}
		}

		if (!noDoneTask) {
			allTasks = obtainAllTasks.toString();
			allTasks.trim();
			isError = false;
			return allTasks;
		} else {
			return allTasks;
		}
	}

	private String searchUndone() {
		boolean noUndoneTask = true;
		String allTasks = SEARCH_UNSUCCESSFUL;
		StringBuilder obtainAllTasks = new StringBuilder();

		obtainAllTasks.append(SEARCH_BY_UNDONE);

		for (Task task : tasks) {
			if (!task.isDone()) {
				obtainAllTasks.append(String.format(DESIRED_TASK,
						tasks.indexOf(task) + BASEINDEX, task.getContent()));
				noUndoneTask = false;
			}
		}

		if (!noUndoneTask) {
			allTasks = obtainAllTasks.toString();
			allTasks.trim();
			isError = false;
			return allTasks;
		} else {
			return allTasks;
		}
	}

	private String searchLabel(String data) {
		boolean noLabelTask = true;
		String allTasks = SEARCH_UNSUCCESSFUL;
		StringBuilder obtainAllTasks = new StringBuilder();
		String label = getLabel(data);
		label.trim();
		obtainAllTasks.append(String.format(SEARCH_BY_LABEL, label));

		for (Task task : tasks) {
			if (task.getLabel() == null) {
			} else if (task.getLabel().equals(label)) {
				obtainAllTasks.append(String.format(DESIRED_TASK,
						tasks.indexOf(task) + BASEINDEX, task.getContent()));
				noLabelTask = false;
			}
		}

		if (!noLabelTask) {
			allTasks = obtainAllTasks.toString();
			allTasks.trim();
			isError = false;
			return allTasks;
		} else {
			return allTasks;
		}
	}

	private String searchContent(String data) {
		boolean noDesiredTask = true;
		String allTasks = SEARCH_UNSUCCESSFUL;
		StringBuilder obtainAllTasks = new StringBuilder();
		String content = data;
		obtainAllTasks.append(String.format(SEARCH_BY_CONTENT, content));

		for (Task task : tasks) {
			if (task.getContent() == null) {
			} else if (task.getContent().contains(content)) {
				obtainAllTasks.append(String.format(DESIRED_TASK,
						tasks.indexOf(task) + BASEINDEX, task.getContent()));
				noDesiredTask = false;
			}
		}

		if (!noDesiredTask) {
			allTasks = obtainAllTasks.toString();
			allTasks.trim();
			isError = false;
			return allTasks;
		} else {
			return allTasks;
		}
	}

	private DateTime obtainDate(String data) throws ParseException {
		DateTimeParser[] parsers = {

		DateTimeFormat.forPattern("dd/MM/yy").getParser(),
				DateTimeFormat.forPattern("dd/MMMM/yy").getParser(),
				DateTimeFormat.forPattern("dd/MM/yyyy").getParser(),
				DateTimeFormat.forPattern("dd/MMMM/yyyy").getParser() };
		DateTimeFormatter formatter = new DateTimeFormatterBuilder().append(
				null, parsers).toFormatter();
		DateTime date = null;
		try {
			date = formatter.parseDateTime(getUserData(data));
		} catch (IllegalFieldValueException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			throw new ParseException(ERROR_INVALID_DATE, 0);
		}
		return date;

	}
	
	private String getLabel(String data) {
		return omitFirstWord(data);
	}
	
	private String omitFirstWord(String sentence) {

		String[] words;
		String trimmedSentence;
		trimmedSentence = sentence.trim();

		words = trimmedSentence.split(" ", 2);
		if (words.length == 2) {
			return words[1];
		} else {
			return "";
		}

	}
	private String getUserData(String userInput) {
		return omitFirstWord(userInput);
	}
	private String getSearchMode(String userData) {
		return obtainFirstWord(userData);
	}
	private String obtainFirstWord(String sentence) {

		String[] words;
		String trimmedSentence;
		trimmedSentence = sentence.trim();

		words = trimmedSentence.split(" ");

		return words[0];

	}

	@Override
	public boolean isError() {
		// TODO Auto-generated method stub
		return isError;
	}



}
